package org.wattdepot.cli;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import org.wattdepot.client.ResourceNotFoundException;
import org.wattdepot.client.WattDepotClient;
import org.wattdepot.client.WattDepotClientException;

/**
 * Processes the list total command and returns the output.
 * 
 * @author Dean Kim
 * 
 */
public class ListTotalCommandCli {

  private static final String LIST_TOTAL_COMMAND_ERROR = "total usage error. Usage: total "
      + "[carbon|energy] {source} day {day} sampling-interval {minutes}";

  protected static final String LIST_TOTAL_NET_ERROR = "There is a network error while "
      + "processing the list total command.";

  /**
   * Verifies required arguments.
   * 
   * @param input Tokenized string command.
   * @return True if arguments are valid and false otherwise.
   */
  protected boolean verifyArguments(String[] input) {

    boolean valid = false;

    if ((input[1].equals("CARBON")) || (input[1].equals("ENERGY"))
        && (input[3].equals("DAY")) && (input[5].equals("SAMPLING-INTERVAL"))) {
      valid = true;
    }

    return valid;
  }

  /**
   * Verifies if Source exists in the WattDepot server.
   * 
   * @param client Valid WattDepot client.
   * @param stringSource Source name.
   * @return True if matching source was found and false if not.
   * @throws WattDepotClientException Throws WattDepotClientException.
   */
  protected boolean verifySource(WattDepotClient client, String stringSource)
      throws WattDepotClientException {

    boolean valid;

    try {
      client.getSource(stringSource);
      valid = true;
    }
    catch (ResourceNotFoundException e) {
      valid = false;
    }

    return valid;
  }

  /**
   * Verifies if WattDepotClient is valid.
   * 
   * @param client WattDepotClient to validate.
   * @return True if WattDepotClient passes validation.
   */
  protected boolean verifyNetwork(WattDepotClient client) {

    boolean valid;
    if (client.isHealthy()) {
      valid = true;
    }
    else {
      valid = false;
    }

    return valid;
  }

  /**
   * Verified date input.
   * 
   * @param inputDate String input of day.
   * @return True if date is valid and false if the date is invalid.
   */
  protected boolean verifyDate(String inputDate) {

    String[] tokenizedString;
    int year;
    int month;
    int day;
    XMLGregorianCalendar xmlCal;

    boolean valid;

    tokenizedString = inputDate.split("-");

    if (tokenizedString.length == 3) {
      try {
        year = Integer.parseInt(tokenizedString[0]);
        month = Integer.parseInt(tokenizedString[1]);
        day = Integer.parseInt(tokenizedString[2]);
        xmlCal = DatatypeFactory.newInstance().newXMLGregorianCalendar();
        xmlCal.setYear(year);
        xmlCal.setMonth(month);
        xmlCal.setDay(day);
      } // end try
      catch (NumberFormatException e) {
        valid = false;
      }
      catch (IllegalArgumentException e) {
        valid = false;
      }
      catch (DatatypeConfigurationException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
      }
      valid = true;
    } // end (tokenizedString.length == 3)
    else {
      valid = false;
    } // end else
    return valid;

  }

  /**
   * Verifies minutes to check if the string is a valid number and the number is greater than 0.
   * 
   * @param stringMinutes Minutes to verify.
   * @return True if minutes is a valid number and greater than 0.
   */
  protected boolean verifyMinutes(String stringMinutes) {

    boolean valid = false;
    int minutes;

    try {
      minutes = Integer.parseInt(stringMinutes);
      if (minutes > 0) {
        valid = true;
      }
    }
    catch (NumberFormatException e) {
      valid = false;
    }

    return valid;
  }

  /**
   * Verifies values of the command line arugments.
   * 
   * @param client Valid WattDepot Client
   * @param source Source passed as a string.
   * @param date Date (YYYY-MM-DD) passed as a string.
   * @param minutes Minutes passed as a string.
   * @return True if value arguments are valid and false if not valid.
   * @throws WattDepotClientException WattDepotClientException thrown.
   */
  protected boolean verifyArgumentValues(WattDepotClient client, String source, String date,
      String minutes) throws WattDepotClientException {

    boolean valid = false;

    if (this.verifySource(client, source) && (this.verifyDate(date))
        && (this.verifyMinutes(minutes))) {
      valid = true;
    } // end if (this.verifySource(client, source))

    return valid;
  }

  /**
   * Produces the XMLGregorianCalendar with time set to the beginning of the day provided.
   * 
   * @param date Date to use.
   * @return XMLGregorianCalendar set to the start of the day provided.
   */
  protected XMLGregorianCalendar getStartDateTime(String date) {

    XMLGregorianCalendar xmlCal = null;
    String[] tokenizedDate = date.split("-");
    try {
      xmlCal = DatatypeFactory.newInstance().newXMLGregorianCalendar();
      xmlCal.setYear(Integer.parseInt(tokenizedDate[0]));
      xmlCal.setMonth(Integer.parseInt(tokenizedDate[1]));
      xmlCal.setDay(Integer.parseInt(tokenizedDate[2]));
      xmlCal.setHour(0);
      xmlCal.setMinute(0);
      xmlCal.setSecond(0);
    }
    catch (DatatypeConfigurationException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    return xmlCal;
  }

  /**
   * Produces the XMLGregorianCalendar with time set to the end of the day provided.
   * 
   * @param date Date to use.
   * @return XMLGregorianCalendar set to the end of the day provided.
   */
  protected XMLGregorianCalendar getEndDateTime(String date) {

    XMLGregorianCalendar xmlCal = null;
    String[] tokenizedDate = date.split("-");
    try {
      xmlCal = DatatypeFactory.newInstance().newXMLGregorianCalendar();
      xmlCal.setYear(Integer.parseInt(tokenizedDate[0]));
      xmlCal.setMonth(Integer.parseInt(tokenizedDate[1]));
      xmlCal.setDay(Integer.parseInt(tokenizedDate[2]));
      xmlCal.setHour(23);
      xmlCal.setMinute(59);
      xmlCal.setSecond(59);
    }
    catch (DatatypeConfigurationException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    return xmlCal;
  }

  /**
   * Retrieves the total carbon generated by a source for a given day where the day is defined
   * between 00:00:00 and 23:59:59.
   * 
   * @param client Valid WattDepotCleint.
   * @param source Valid source.
   * @param date Valid date (YYYY-MM-DD).
   * @param interval Minutes specifying sampling interval.
   * @return Total amount of carbon generated by a source.
   * @throws WattDepotClientException Throws WattDepotClientException.
   */
  protected double getTotalSourceCarbon(WattDepotClient client, String source, String date,
      String interval) throws WattDepotClientException {

    double totalCarbon;

    totalCarbon = client.getCarbonEmitted(source, this.getStartDateTime(date), this
        .getEndDateTime(date), Integer.parseInt(interval));

    return (totalCarbon);

  }

  /**
   * Retrieves the total energy generated by a source for a given day where the day is defined
   * between 00:00:00 and 23:59:59.
   * 
   * @param client Valid WattDepotCleint.
   * @param source Valid source.
   * @param date Valid date (YYYY-MM-DD).
   * @param interval Minutes specifying sampling interval.
   * @return Total amount of energy generated by a source.
   * @throws WattDepotClientException Throws WattDepotClientException.
   */
  protected double getTotalSourceEnergy(WattDepotClient client, String source, String date,
      String interval) throws WattDepotClientException {

    double totalEnergy = 0;

    totalEnergy = client.getEnergyGenerated(source, this.getStartDateTime(date), this
        .getEndDateTime(date), Integer.parseInt(interval));

    return (totalEnergy);
  }

  /**
   * Entry and return point for processing the list total command.
   * 
   * @param client Valid WattDepot client.
   * @param input Tokenized command.
   * @return Formatted output for the respective commands.
   * @throws WattDepotClientException Throws WattDepotClientException.
   */
  protected String processCommand(WattDepotClient client, String[] input)
      throws WattDepotClientException {

    String commandOutput = LIST_TOTAL_COMMAND_ERROR;
    double carbonOutput;
    double energyOutput;

    if (this.verifyNetwork(client)) {

      if ((input.length == 7) && this.verifyArguments(input)
          && (this.verifyArgumentValues(client, input[2], input[4], input[6]))) {
        if (input[1].equals("CARBON")) {
          carbonOutput = this.getTotalSourceCarbon(client, input[2], input[4], input[6]);
          commandOutput = Double.toString(carbonOutput);
        } // end if (input[2].equals("CARBON"))
        if (input[1].equals("ENERGY")) {
          energyOutput = this.getTotalSourceEnergy(client, input[2], input[4], input[6]);
          commandOutput = Double.toString(energyOutput);
        } // end if (input[2].equals("ENERGY"))
      } // end (this.verifyArguments(input) && ...

    } // end if (this.verifyNetwork(client))
    else {
      commandOutput = LIST_TOTAL_NET_ERROR;
    }

    return commandOutput;
  }
}
